﻿//// Copyright (c) Microsoft Corporation. All rights reserved

(function () {
    "use strict";

    var d = Windows.Foundation.Diagnostics;

    /// <summary>
    /// Demonstrate usage of LoggingChannel and LoggingActivity.
    /// </summary>
    var LoggingChannelScenario = WinJS.Class.define(
        function () {
        },
        {
            /// <summary>
            /// Construct a LoggingChannel with the Windows 8.1 constructor, then
            /// use the LoggingChannel for the scenario.
            /// See this sample's README for further discussion.
            /// </summary>
            logWithWin81Constructor: function () {
                /*
                If a LoggingChannel is used as a local or member variable, it should
                be closed (disposed) when no longer needed.
                */

                {
                    // The 1-parameter constructor creates a channel with Windows 8.1 semantics.
                    var channel = new d.LoggingChannel("SampleProvider");

                    // The Id for a Windows 8.1-mode channel is always the same.
                    // channel.Id == 4bd2826e-54a1-4ba9-bf63-92b73ea1ac4a

                    this.demonstrateLogging(channel);
                    channel.close();
                }
            },

            /// <summary>
            /// Construct a LoggingChannel with the Windows 10 constructor, then
            /// use the LoggingChannel for the scenario. Also show the use cases
            /// for the two Windows 10 constructors.
            /// See this sample's README for further discussion.
            /// </summary>
            logWithWin10Constructor: function () {
                /*
                If a LoggingChannel is used as a local or member variable, it should
                be closed (disposed) when no longer needed.
                */

                var channel;

                // The 2-parameter constructor creates a channel with Windows 10 semantics.
                channel = new d.LoggingChannel(
                    "SampleProvider",
                    null); // null means use default options.

                // The Id is generated by hashing the string "SampleProvider".
                // channel.Id == eff1e128-4903-5093-096a-bdc29b38456f

                this.demonstrateLogging(channel);

                channel.close();

                /*
                Demonstrate other (less-common) constructor scenarios:
                */

                // This creates a channel with Windows 10 semantics and declared
                // membership in a provider group.
                channel = new d.LoggingChannel(
                    "SampleProvider",
                    new d.LoggingChannelOptions("2e0582f3-d1b6-516a-9de3-9fd79ef952f8")); // Join a provider group

                // The Id is generated by hashing the string "SampleProvider".
                // channel.Id == eff1e128-4903-5093-096a-bdc29b38456f

                // This creates a channel with Windows 10 semantics and a specific
                // provider Id.
                var channel = new d.LoggingChannel(
                    "SampleProvider",
                    null,
                    "2e0582f3-d1b6-516a-9de3-9fd79ef952f8");
                // channel.Id == 2e0582f3-d1b6-516a-9de3-9fd79ef952f8
            },

            /// <summary>
            /// This method demonstrates the LoggingChannel and LoggingActivity APIs.
            /// </summary>
            /// <param name="channel">
            /// The channel to use for the demonstration. This channel may have been
            /// constructed using a Windows 8.1 constructor or a Windows 10 constructor.
            /// The same APIs are supported in both cases, but the ETL events will be
            /// formatted a bit differently depending on how the channel was constructed.
            /// </param>
            demonstrateLogging: function (channel) {
                // Whenever any ETW session changes the way it is listening to this
                // channel, the LoggingEnable event is fired. For example, this might
                // be called when a session begins listening, changes the level at
                // which it is listening, or stops listening.
                channel.addEventListener("loggingenabled", this.onLoggingEnabled);

                // Log simple string events
                channel.logMessage("Simple message"); // default level is Verbose
                channel.logMessage("Simple error", d.LoggingLevel.error);

                // Log simple string + integer events.
                channel.logValuePair("Simple message", 123); // default level is Verbose
                channel.logValuePair("Simple error", 456, d.LoggingLevel.error);

                // The channel.Name property returns the name that was used when the
                // channel was constructed. When running in Windows 10 mode, the name
                // is already set as the provider name, so no LoggingChannelName is
                // automatically added to the event.
                channel.logMessage(channel.name);

                // The channel.Id property is new to Windows 10.
                channel.logMessage(channel.id.toString());

                // If you want to avoid the overhead of collecting data when nobody is
                // listening to your channel, check the Enabled property before logging.
                if (channel.enabled) {
                    channel.logMessage(this.collectExpensiveData());
                }

                // The IsEnabled() method is exactly the same as the Enabled property,
                // except that it is a new Windows 10 API.
                if (channel.isEnabled()) {
                    channel.logMessage(this.collectExpensiveData());
                }

                // If you want to only collect data if somebody is listening at a specific
                // level, you need to check both Enabled and Level. Note that the value of
                // the Level is unspecified when Enabled is false.
                if (channel.enabled && channel.level <= d.LoggingLevel.warning) {
                    channel.logMessage(this.collectExpensiveData(), d.LoggingLevel.warning);
                }

                // The IsEnabled(LoggingLevel) method is a bit nicer than checking both
                // Enabled and Level, but it is only available on Windows 10 or later.
                if (channel.isEnabled(d.LoggingLevel.warning)) {
                    channel.logMessage(this.collectExpensiveData(), d.LoggingLevel.warning);
                }

                // You can also use IsEnabled to check for keywords.
                if (channel.isEnabled(d.LoggingLevel.information, 0x10)) {
                    channel.logMessage(this.collectExpensiveData(), d.LoggingLevel.information);
                }

                // Use LoggingFields with the LogEvent method to write complex events.
                var fields = new d.LoggingFields();
                fields.addDouble("pi", 3.14159);
                channel.logEvent(
                    "ComplexEvent",
                    fields,
                    d.LoggingLevel.verbose,
                    new d.LoggingOptions(0x10)); // Keywords = 0x10

                // You can add any number of name-value pairs to a fields object, though
                // you may encounter ETW limitations if you add too many. For example,
                // ETW is limited to a maximum event size of 64KB, and the current
                // TraceLogging decoder can handle no more than 128 fields.

                // Performance optimization: You can reuse a LoggingFields object to
                // avoid unnecessary allocations. Don't forget to call Clear()
                // between uses, and don't try to share a LoggingFields object between
                // threads.
                fields.clear();
                fields.addDateTime("Date", new Date());
                channel.logEvent("Now", fields);

                fields.clear();

                // You can add a formatting hint to affect the way a value is decoded.
                // Not all combinations are useful, and the hint may be ignored.
                // For example, you can encode an MBCS string by writing a byte array
                // with a String hint.
                fields.addUInt8Array(
                    "AnsiString",
                    [ 65, 66, 67, 49, 50, 51 ], // "ABC123"
                    d.LoggingFieldFormat.string);

                // You can add "tag" bits to a field. These are user-defined bits that
                // can be used to communicate with an event processing tool. For example,
                // you might define a tag bit to indicate that a field contains private
                // data that should not be displayed on-screen.
                fields.addString("Password", "12345", d.LoggingFieldFormat.default, 0x10);

                // You can add a "structure" to an event. A structure is a name for a
                // group of fields. Structures can nest. Call BeginStruct to add a level
                // of nesting, and call EndStruct after the last field of the structure.
                fields.beginStruct("Nested");
                    fields.addInt16("Nested-1", 1);
                    fields.addInt16("Nested-2", 2);
                    fields.beginStruct("Nested-Nested");
                        fields.addInt16("Nested-Nested-3", 3);
                    fields.endStruct();
                    fields.addInt16("Nested-4", 4);
                fields.endStruct();

                // Advanced scenarios: you can use a LoggingOptions object to control
                // detailed event settings such as keywords, opcodes, and activity Ids.
                // These have their normal ETW semantics. You can also set event tags,
                // which are bit values that can be used to communicate with the event
                // processor.
                var options = new d.LoggingOptions();
                options.keywords = 0x123;
                options.tags = 0x10;
                channel.logEvent(
                    "VeryComplexEvent",
                    fields,
                    d.LoggingLevel.information,
                    options);

                // Windows 10 introduces the ILoggingTarget interface. LoggingChannel
                // implements this interface. This interface allows components to accept
                // a logger as an parameter.
                this.doSomething(channel);

                /*
                If a LoggingActivity is created using a LoggingActivity constructor,
                it will use Windows 8.1 semantics:

                - If an activity is destroyed (garbage-collected) without being closed
                  and the associated LoggingChannel is still open, the activity will
                  write a default Stop event.
                - The default Stop event (written by the destructor or by the Close()
                  method) is encoded as a "simple" event.

                The 8.1 semantics are deprecated because the automatic generation of
                a Stop event at garbage-collection can be misleading. The Stop event
                is intended to mark the a precise point at which an activity is
                completed, while the garbage-collection of an abandoned activity is
                inherently imprecise and unpredictable.

                If a LoggingActivity is created using a StartActivity method, it will
                use Windows 10 semantics:

                - If an activity is destroyed (garbage-collected) without being closed,
                  there will be no Stop event for the activity.
                - The default Stop event (written by the Close() method) is encoded as
                  a TraceLogging event with name "ActivityClosed".
                */

                // This activity is created with Windows 8.1 semantics.
                var a1 = new d.LoggingActivity("Activity1", channel);

                // The activity Start event is written by the LoggingActivity constructor.
                // You would do your activity's work here.
                // The activity Stop event is written when the activity is closed (disposed).

                // The Windows 10 LoggingActivity adds new methods for writing events
                // that are marked as associated with the activity.
                a1.logEvent("Activity event");

                // LoggingActivity also implements the ILoggingTarget interface, so you can
                // use either a channel or an activity as a logging target.
                this.doSomething(a1);

                // The Windows 10 LoggingActivity adds new methods for creating nested activities.
                // Note that nested activities are always created with Windows 10 semantics,
                // even when nested under an activity that is using Windows 8.1 semantics.
                var a2 = a1.startActivity("Activity2");

                // Nested task occurs here.

                // The Windows 10 LoggingActivity allows you to customize the Stop event.
                a2.stopActivity("Activity 2 stop");

                // Because a1 is using Windows 8.1 semantics, if we did not call close(),
                // it would attempt to write a Stop event when it is garbage collected.
                // Writing Stop events during garbage collection is not useful, so be sure
                // to properly stop, close, or dispose activities.
                a1.close();

                // The Windows 10 StartActivity method creates a new activity, optionally with
                // specified fields and characteristics.
                // This activity is created with Windows 10 semantics.
                var a3 = channel.startActivity("Activity3");

                // Because a3 is using Windows 10 semantics, if we did not call close(),
                // there would be no Stop event (not even when the activity is garbage
                // collected). To get a Stop event, be sure to stop, close, or dispose the
                // activity.
                a3.close();
            },

            collectExpensiveData: function () {
                return "ExpensiveData";
            },

            doSomething: function (logger) {
                logger.logEvent("Did something");
            },

            onLoggingEnabled: function (sender, args) {
                // Here, you could note a change in the level or keywords.
            }
        },
        {
            // LoggingChannelScenario static members
        });

    WinJS.Class.mix(LoggingChannelScenario, WinJS.Utilities.eventMixin);

    // Export public methods & controls
    WinJS.Namespace.define("LoggingChannelScenario", {
        LoggingChannelScenario: LoggingChannelScenario
    });
})();
